﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;

namespace AMS.Common
{
    /// <summary>
    /// XML操作类
    /// </summary>
    public class XmlOperation
    {
        /// <summary>
        /// 
        /// </summary>
        public XmlOperation()
        {
            xdoc = new XmlDocument();
        }

        #region 字段和属性
        /// <summary>
        /// 
        /// </summary>
        XmlDocument xdoc;

        /// <summary>
        /// XML document 对象
        /// </summary>
        public XmlDocument XmlDoc
        {
            get { return xdoc; }
        }

        /// <summary>
        /// 代表错误的字段
        /// </summary>
        public static readonly string ErrorText = "<error>";
        #endregion

        #region 仅有方法
        /// <summary>
        /// 加载文件
        /// </summary>
        /// <param name="file"></param>
        public void Load(string file)
        {
            xdoc.Load(file);
        }

        /// <summary>
        /// 加载对象
        /// </summary>
        /// <param name="xml"></param>
        public void LoadXml(string xml)
        {
            xdoc.LoadXml(xml);
        }

        /// <summary>
        /// 保存xml到文件
        /// </summary>
        /// <param name="fullFilePath"></param>
        public void SaveToFile(string fullFilePath)
        {
            xdoc.Save(fullFilePath);
        }

        /// <summary>
        /// 添加或更新节点内容
        /// </summary>
        /// <param name="xpath"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public XmlNode AddOrUpdateNode(string xpath, string value)
        {
            XmlNode node = xdoc.SelectSingleNode(xpath);
            if (node == null)
            {
                node = CreateNodeByPath(xpath);
            }

            node.InnerText = value;
            return node;
        }

        /// <summary>
        /// 添加（更新）一个节点
        /// </summary>
        /// <param name="parentNodeXPath"></param>
        /// <param name="node"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public XmlNode AddOrUpdateNode(string parentNodeXPath, string node, string value)
        {
            XmlNode parentNode = xdoc.SelectSingleNode(parentNodeXPath);
            if (parentNode == null)
            {
                return null;
            }

            XmlNode self = parentNode.SelectSingleNode(node);
            if (self == null)
            {
                self = xdoc.CreateNode(XmlNodeType.Element, node, null);
                parentNode.AppendChild(self);
            }

            self.InnerXml = value;

            return self;
        }

        /// <summary>
        /// 添加或更新节点
        /// </summary>
        /// <param name="parentNodeXPath"></param>
        /// <param name="node"></param>
        /// <param name="attributeName"></param>
        /// <param name="attributeValue"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public XmlNode AddOrUpdateNode(string parentNodeXPath, string node, string attributeName, string attributeValue, string value)
        {
            XmlNode self = AddOrUpdateNode(parentNodeXPath, node, value);
            if (self != null)
            {
                XmlAttribute attribute = self.Attributes[attributeName];
                if (attribute == null)
                {
                    attribute = (XmlAttribute)xdoc.CreateNode(XmlNodeType.Attribute, attributeName, null);
                    self.Attributes.Append(attribute);
                }

                attribute.Value = attributeValue;
            }

            return self;
        }

        /// <summary>
        /// 删除节点
        /// </summary>
        /// <param name="xpath"></param>
        public void DeleteNode(string xpath)
        {
            XmlNode node = xdoc.SelectSingleNode(xpath);
            if (node != null && node != xdoc.DocumentElement)
            {
                node.ParentNode.RemoveChild(node);
            }
        }

        /// <summary>
        /// 获取节点的值。
        /// 返回NULL表示节点不存在，如果是string.Empty，则表示节点存在，但值为空。
        /// </summary>
        /// <param name="xpath"></param>
        /// <returns></returns>
        public string GetNodeValue(string xpath)
        {
            XmlNode node = xdoc.SelectSingleNode(xpath);
            if (node != null)
            {
                return node.InnerText == null ? string.Empty : node.InnerText;
            }

            return null;
        }

        /// <summary>
        /// 获取节点的属性值，如果节点不存在或属性不存在，返回 null. 否则返回具体的值或 string.Empty
        /// </summary>
        /// <param name="xpath"></param>
        /// <param name="attributeName"></param>
        /// <returns></returns>
        public string GetAttribteValue(string xpath, string attributeName)
        {
            XmlNode node = xdoc.SelectSingleNode(xpath);
            if (node != null)
            {
                XmlAttribute attribute = node.Attributes[attributeName];
                if (attribute != null)
                {
                    return attribute.Value;
                }
            }

            return null;
        }

        /// <summary>
        /// 判断XML节点是否具有某个属性
        /// </summary>
        /// <param name="xpath"></param>
        /// <param name="attributeName"></param>
        /// <returns></returns>
        public bool HasAttribute(string xpath, string attributeName)
        {
            XmlNode node = xdoc.SelectSingleNode(xpath);
            if (node != null)
            {
                XmlAttribute attribute = node.Attributes[attributeName];
                return attribute != null;
            }

            return false;
        }

        /// <summary>
        /// 设置节点的属性值。如果节点不存在，则返回false. 否则如果属性不存在，则会创建，如果存在则直接设置节点的值，返回true.
        /// </summary>
        /// <param name="xpath"></param>
        /// <param name="attributeName"></param>
        /// <param name="attributeValue"></param>
        /// <returns></returns>
        public bool SetNodeAttribute(string xpath, string attributeName, string attributeValue)
        {
            XmlNode node = xdoc.SelectSingleNode(xpath);
            if (node == null)
            {
                return false;
            }

            XmlAttribute attribute = node.Attributes[attributeName];
            if (attribute == null)
            {
                attribute = (XmlAttribute)xdoc.CreateNode(XmlNodeType.Attribute, attributeName, null);
                node.Attributes.Append(attribute);
            }

            attribute.Value = attributeValue;
            return true;
        }

        /// <summary>
        /// 判断节点是否存在
        /// </summary>
        /// <param name="xpath"></param>
        /// <returns></returns>
        public bool IsNodeExists(string xpath)
        {
            return IsNodeExists(xdoc.DocumentElement, xpath);
        }

        /// <summary>
        /// 判断节点是否存在
        /// </summary>
        /// <param name="rootNode"></param>
        /// <param name="xpath"></param>
        /// <returns></returns>
        public bool IsNodeExists(XmlNode rootNode, string xpath)
        {
            return rootNode.SelectSingleNode(xpath) != null;
        }

        /// <summary>
        /// 将XML导出成字符串(不进行缩进格式化)
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return xdoc.OuterXml;
        }

        /// <summary>
        /// 将XML导出成字符串(根据标识是否进行缩进格式化)
        /// </summary>
        /// <param name="wellFormatted"></param>
        /// <returns></returns>
        public string ToString(bool wellFormatted)
        {
            if (!wellFormatted)
            {
                return ToString();
            }
            else
            {
                StringBuilder sbcontent = new StringBuilder();
                StringWriter writer = new StringWriter(sbcontent);
                xdoc.Save(writer);
                writer.Close();
                return sbcontent.ToString();
            }
        }
        #endregion

        #region Static 方法
        /// <summary>
        /// 获取移除<see cref="ErrorText"/>后干净我字符串
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ClearText(string text)
        {
            if (string.Equals(text, ErrorText, StringComparison.InvariantCultureIgnoreCase))
            {
                return null;
            }

            return null;
        }
        #endregion

        #region 内部方法
        XmlNode CreateNodeByPath(string xpath)
        {
            return CreateNodeByPath(null, xpath);
        }

        XmlNode CreateNodeByPath(XmlNode node, string xpath)
        {
            XmlNode childNode = null;
            if (xpath.StartsWith("/"))
            {
                int index = xpath.IndexOf("/", 1);
                if (index == -1)
                {
                    return xdoc.DocumentElement;
                }
                else
                {
                    xpath = xpath.Remove(0, index + 1);
                    node = xdoc.DocumentElement;
                }
            }

            string[] segments = xpath.Split('/');
            if (xdoc.DocumentElement == null)
            {
                xdoc.LoadXml(string.Format("<{0}></{1}>", segments[0], segments[0]));
            }

            if (node == null)
            {
                node = xdoc.DocumentElement;
            }

            foreach (string nodeName in segments)
            {
                if (string.IsNullOrEmpty(nodeName)) continue;

                childNode = GetDirectChild(node, nodeName);
                if (childNode != null)
                {
                    node = childNode;
                }
                else
                {
                    childNode = xdoc.CreateElement(nodeName);
                    node.AppendChild(childNode);
                    node = childNode;
                }
            }

            return childNode;
        }

        XmlNode GetDirectChild(XmlNode node, string childNodeName)
        {
            foreach (XmlNode child in node.ChildNodes)
            {
                if (child.Name == childNodeName)
                {
                    return child;
                }
            }

            return null;
        }
        #endregion
    }
}
